package daoImpl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import org.apache.log4j.Logger;

import queries.QueriesPessoa;
import util.Helper;
import constants.Constants;
import dao.PessoaDAO;
import entities.Apartamento;
import entities.Pessoa;
import exceptions.CPFInvalidoException;
import exceptions.CampoVazioException;
import exceptions.RGInvalidoException;
import exceptions.TamanhoInvalidoException;
import exceptions.TipoNaoPermitidoException;

public class PessoaDAOImpl implements PessoaDAO {

	private static Logger log = Logger.getLogger(PessoaDAOImpl.class);

	private static PessoaDAOImpl dao = null;

	private PessoaDAOImpl(){
	}

	public static PessoaDAOImpl getInstance(){
		if(dao == null){
			dao = new PessoaDAOImpl();
		}
		return dao;
	}


	@Override
	public ArrayList<String> listarMoradoresEmApto(String numero, String bloco, boolean recuperarNome,
			Connection connection) throws SQLException {

		log.debug("Inicio do metodo PessoaDAOImpl.listarMoradoresEmApto." + Constants.LINE_SEPARATOR +
				"Listando moradores para o apartamento de numero <" + numero + "> e bloco <" + bloco + ">.");

		PreparedStatement pstm;
		pstm = connection.prepareStatement(QueriesPessoa.LISTAR_MORADORES_EM_APTO);

		pstm.setString(1, numero);
		pstm.setString(2, bloco);


		ResultSet rs = pstm.executeQuery();

		ArrayList<String> moradores = new ArrayList<String>();

		while(rs.next()){
			if(recuperarNome){
				moradores.add(rs.getString("NOME"));
			}
			else{
				moradores.add(rs.getString("CPF"));
			}
		}

		pstm.close();
		rs.close();

		return moradores;

	}

	/**
	 * @throws SQLException
	 * @see dao.PessoaDAO#recuperaCPFFiador(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public String recuperaCPFFiador(String cpfLocatario, String numero, String bloco, Connection connection) throws SQLException{

		log.debug("Inicio do metodo PessoaDAOImpl.recuperaCPFFiador." + Constants.LINE_SEPARATOR +
				"Buscando o cpf do fiador referente ao contrato com cpf de locatario < " + cpfLocatario + " > e apartamento < " + numero + " " + bloco + "!");

		String retorno = null;

		PreparedStatement pstm;
		pstm = connection.prepareStatement(QueriesPessoa.BUSCAR_CPF_FIADOR);

		pstm.setString(1, cpfLocatario);
		pstm.setString(2, numero);
		pstm.setString(3, bloco);

		ResultSet rs =  pstm.executeQuery();

		if(rs.next()){
			retorno = rs.getString("cpfFiador");
		}

		pstm.close();
		rs.close();

		return retorno;
	}

	/**
	 * @throws SQLException
	 * @see dao.PessoaDAO#recuperaCPFFiador(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public String recuperaCPF(String nome, Connection connection) throws SQLException{

		log.debug("Inicio do metodo PessoaDAOImpl.recuperaCPF." + Constants.LINE_SEPARATOR +
				"Buscando o cpf referente a pessoa de nome < " + nome + " >!");

		String retorno = null;

		PreparedStatement pstm;
		pstm = connection.prepareStatement(QueriesPessoa.BUSCAR_CPF_PESSOA);

		pstm.setString(1, nome);

		ResultSet rs =  pstm.executeQuery();

		if(rs.next()){
			retorno = rs.getString("cpf");
		}

		pstm.close();
		rs.close();

		return retorno;
	}

	public void atualizaStatusMorador(String cpfLocatario, int status, Connection connection) throws SQLException {

		log.debug("Inicio do metodo DAOImpl.atualizaStatusMorador. " +
				"Atualizando o status do locatario de CPF < " + cpfLocatario + " > para < " + Helper.getStatusName(status));

		PreparedStatement pstm = connection.prepareStatement(Queries.ATUALIZA_STATUS_INQUILINO);

		pstm.setInt(1, status);
		pstm.setString(2, cpfLocatario);

		pstm.executeUpdate();

		log.debug("Atualizacao realizada com sucesso!");

	}

	/**
	 * @see dao.PessoaDAO#atualizarStatus(java.lang.String, java.sql.Connection)
	 */
	@Override
	public void atualizarStatus(String cpfLocatario, int status, Connection connection) throws SQLException {

		log.debug("Inicio do metodo PessoaDAOImpl.atualizarStatus." + Constants.LINE_SEPARATOR +
				"Atualizado o status do morador de cpf < " + cpfLocatario + " > para o status < " + Helper.getStatusName(status) + " >!");

		String retorno = null;

		PreparedStatement pstm;
		pstm = connection.prepareStatement(QueriesPessoa.ATUALIZAR_STATUS_MORADOR);

		pstm.setInt(1, status);
		pstm.setString(2, cpfLocatario);

		pstm.executeUpdate();

		pstm.close();
	}

	@Override
	public void alterarMoradorApto(String cpf, Apartamento aptoOrigem, Apartamento aptoDestino,
			Connection connection) throws SQLException {
		log.debug("Inicio do metodo ApartamentoDAOImpl.alterarMoradorApto. Alterando o morador " +
				" de cpf <"+ cpf + "> do apartamento <" + aptoOrigem.getNumero() + " " + aptoOrigem.getBloco() + " " +
				"para o apartamento de numero < " + aptoDestino.getNumero() + " " + aptoDestino.getBloco() + " >.");

		PreparedStatement pstm = connection.prepareStatement(Queries.ALTERA_MORADOR_DE_APARTAMENTO);

		pstm.setString(1, aptoDestino.getNumero());
		pstm.setString(2, aptoDestino.getBloco());
		pstm.setString(3, cpf);

		pstm.executeUpdate();

		log.debug("Morador atualizado com sucesso!");

	}

	public void cadastrarMorador(String nome, String endereco, String telefone,
			String celular, String fax, String rg, String cpf, String renda,
			String email, boolean isInquilino, Connection connection) throws CPFInvalidoException, RGInvalidoException,
			CampoVazioException, SQLException, TamanhoInvalidoException, TipoNaoPermitidoException {

		log.debug("Inicio do metodo DAOImpl.cadastrarMorador. Iniciando o cadastro dos seguintes dados: " + Constants.LINE_SEPARATOR +
				"Nome: " + nome + Constants.LINE_SEPARATOR +
				"Endereco: " +  endereco + Constants.LINE_SEPARATOR +
				"Telefone: " + telefone + Constants.LINE_SEPARATOR +
				"Celular: " + celular + Constants.LINE_SEPARATOR +
				"Fax: " + fax + Constants.LINE_SEPARATOR +
				"RG: " + rg + Constants.LINE_SEPARATOR +
				"CPF: " + cpf + Constants.LINE_SEPARATOR +
				"Renda: " + renda + Constants.LINE_SEPARATOR +
				"Email: " + email);


		Helper.validarCadastroMorador(nome, endereco, rg,cpf, telefone, celular, renda, email, new Pessoa(),isInquilino, true);

		PreparedStatement pstm;
		pstm = connection.prepareStatement(Queries.CADASTRAR_PESSOA);

		pstm.setString(1, nome);
		pstm.setString(2, endereco);
		pstm.setString(3, telefone);
		pstm.setString(4, celular);
		pstm.setString(5, fax);
		pstm.setString(6, rg);
		pstm.setString(7, cpf);
		if(Constants.EMPTY_STRING.equals(renda)){
			pstm.setDouble(8, Double.valueOf("0"));
		}
		else{
			pstm.setDouble(8, Double.valueOf(renda));
		}

		pstm.setString(9, email);
		if(isInquilino){
			pstm.setInt(10, Constants.CADASTRADO);
		}
		else{
			pstm.setInt(10, Constants.FIADOR_INATIVO);
		}
		pstm.setBoolean(11, isInquilino);

		pstm.executeUpdate();

		pstm.close();
	}

	public ArrayList<String> listarPessoas(boolean isInquilino, int status, Connection connection) throws SQLException{

		log.debug("Inicio do metodo DAOImpl.listarPessoas.");

		ArrayList<String> retorno = new ArrayList<String>();

		PreparedStatement pstm;

		pstm = connection.prepareStatement(Queries.LISTA_PESSOAS);
		pstm.setBoolean(1, isInquilino);
		pstm.setInt(2, status);

		ResultSet rs = pstm.executeQuery();

		while (rs.next()){
			retorno.add(rs.getString("NOME"));
		}

		pstm.close();

		return retorno;
	}

	@Override
	public ResultSet listarMoradores(String nome,
			Connection connection) throws SQLException {

		log.debug("Inicio do metodo PessoaDAOImpl.listarMoradores." + Constants.LINE_SEPARATOR +
				"Listando moradores com o nome <" + nome + ">");

		PreparedStatement pstm;

		pstm = connection.prepareStatement(QueriesPessoa.LISTAR_MORADORES);

		pstm.setString(1, "%".concat(nome).concat("%"));

		return pstm.executeQuery();

	}

	@Override
	public Pessoa recuperaDadosMorador(String cpf, Connection connection) throws SQLException {

		log.debug("Recuperando dados para o cpf <" + cpf + ">");

		PreparedStatement pstm = connection.prepareStatement(QueriesPessoa.RECUPERA_DADOS_MORADOR);

		pstm.setString(1, cpf);

		ResultSet rs = pstm.executeQuery();

		Pessoa pessoa = new Pessoa();

		if(rs.next()){
			pessoa.setNome(rs.getString("nome"));
			pessoa.setCpf(rs.getString("cpf"));
			pessoa.setEndereco(rs.getString("endereco"));
			pessoa.setTelefone(rs.getString("telefone"));
			pessoa.setCelular(rs.getString("celular"));
			pessoa.setFax(rs.getString("fax"));
			pessoa.setRg(rs.getString("rg"));
			pessoa.setRenda(rs.getString("renda"));
			pessoa.setEmail(rs.getString("email"));
			pessoa.setEhInquilino(rs.getBoolean("eh_inquilino"));
			pessoa.setStatus(rs.getInt("status"));
		}

		pstm.close();
		rs.close();

		return pessoa;

	}

	@Override
	public void atualizarMorador(String nome, String endereco, String telefone,
			String celular, String fax, String rg, String cpf, String renda,
			String email, boolean ehInquilino, Connection connection) throws SQLException, CPFInvalidoException, RGInvalidoException, CampoVazioException, TamanhoInvalidoException, TipoNaoPermitidoException {

		log.debug("Inicio do metodo DAOImpl.atualizarMorador. Iniciando a atualizacao dos seguintes dados: " + Constants.LINE_SEPARATOR +
				"Nome: " + nome + Constants.LINE_SEPARATOR +
				"Endereco: " +  endereco + Constants.LINE_SEPARATOR +
				"Telefone: " + telefone + Constants.LINE_SEPARATOR +
				"Celular: " + celular + Constants.LINE_SEPARATOR +
				"Fax: " + fax + Constants.LINE_SEPARATOR +
				"RG: " + rg + Constants.LINE_SEPARATOR +
				"Renda: " + renda + Constants.LINE_SEPARATOR +
				"Email: " + email + Constants.LINE_SEPARATOR +
				"Inqulino: " + ehInquilino);


		Pessoa pessoa = recuperaDadosMorador(cpf, connection);

		Helper.validarCadastroMorador(nome, endereco, rg,cpf, telefone, celular, renda, email, pessoa, ehInquilino, false);

		PreparedStatement pstm;
		pstm = connection.prepareStatement(Queries.ATUALIZAR_DADOS);

		pstm.setString(1, nome);
		pstm.setString(2, endereco);
		pstm.setString(3, telefone);
		pstm.setString(4, celular);
		pstm.setString(5, fax);
		pstm.setString(6, rg);
		if(Constants.EMPTY_STRING.equals(renda)){
			pstm.setDouble(7, Double.valueOf("0"));
		}
		else{
			pstm.setDouble(7, Helper.trunc(Double.valueOf(renda),2));
		}

		pstm.setString(8, email);
		pstm.setBoolean(9, ehInquilino);

		if(ehInquilino != pessoa.isEhInquilino() && ehInquilino){
			pstm.setInt(10, Constants.CADASTRADO);
		}
		else if(ehInquilino != pessoa.isEhInquilino() && !ehInquilino){
			pstm.setInt(10, Constants.FIADOR_INATIVO);
		}
		else{
			pstm.setInt(10, pessoa.getStatus());
		}

		pstm.setString(11, cpf);

		pstm.executeUpdate();

		pstm.close();
	}

	@Override
	public void excluirPessoa(String cpfLocatario, Connection connection)
	throws SQLException {

		Pessoa p = recuperaDadosMorador(cpfLocatario, connection);
		PreparedStatement pstm;
		pstm = connection.prepareStatement(QueriesPessoa.EXCLUI_PESSOA);

		log.info("Excluindo da base a seguinte pessoa: \n" + p);

		try{
			pstm.setString(1, cpfLocatario);
			pstm.executeUpdate();
		}finally {
			pstm.close();
		}

	}

}
